import numpy as np
import torch


def crossover_2point_bit(self):
    Chrom, size_pop, len_chrom = self.Chrom, self.size_pop, self.len_chrom
    half_size_pop = int(size_pop / 2)
    Chrom1, Chrom2 = Chrom[:half_size_pop], Chrom[half_size_pop:]
    # mask=random_bit_mask(self.device,self.Lind,self.number_type,0.03,half_size_pop,len_chrom,6)
    mask = torch.randint(np.iinfo(self.number_type).min, np.iinfo(self.number_type).max, (half_size_pop, len_chrom),
                         dtype=self.gpu_number_type, device=self.device)
    mask2 = (Chrom1 ^ Chrom2) & mask
    Chrom1 ^= mask2
    Chrom2 ^= mask2
    return self.Chrom


def crossover_pmx_vrptw(self):
    tmpChrom_index = self.Chrom != 0
    tmpChrom = self.Chrom[tmpChrom_index].reshape(self.size_pop, -1) - 1
    tmpChrom = tmpChrom.long()

    Chrom, size_pop, len_chrom = self.Chrom, self.size_pop, self.len_chrom

    cxpoint1, cxpoint2 = torch.randint(0, self.len_chrom - 1, (2, size_pop // 2), device=self.device)
    cxswap = cxpoint1 >= cxpoint2
    cxpoint1[cxswap], cxpoint2[cxswap] = cxpoint2[cxswap], cxpoint1[cxswap] + 1
    pos_recorder = torch.zeros_like(tmpChrom)
    pos_recorder[
        torch.div(
            torch.arange(
                len(pos_recorder.view(-1)), device=self.device
            ).reshape([-1, self.len_chrom]),
            self.len_chrom,
            rounding_mode='floor'),
        tmpChrom.long()
    ] = torch.arange(self.len_chrom).type_as(tmpChrom)
    # carlist cross
    for i in range(0, self.len_chrom):
        sel = torch.where((cxpoint1 <= i) & (i < cxpoint2))[0]
        sel1, sel2 = sel * 2, sel * 2 + 1

        value1, value2 = tmpChrom[sel1, i], tmpChrom[sel2, i]
        pos1, pos2 = pos_recorder[sel1, value2], pos_recorder[sel2, value1]
        tmpChrom[sel1, i], tmpChrom[sel1, pos1] = tmpChrom[sel1, pos1], tmpChrom[sel1, i]
        tmpChrom[sel2, i], tmpChrom[sel2, pos2] = tmpChrom[sel2, pos2], tmpChrom[sel2, i]
        pos_recorder[sel1, value1], pos_recorder[sel1, value2] = pos_recorder[sel1, value2], i
        pos_recorder[sel2, value1], pos_recorder[sel2, value2] = i, pos_recorder[sel2, value1]

    # deltaT cross
    self.tmpChrom_index = tmpChrom_index
    self.tmpChrom = tmpChrom.type_as(self.Chrom) + 1
    return self.Chrom


def crossover_pmx(self):
    self.Chrom = self.Chrom.long()

    Chrom, size_pop, len_chrom = self.Chrom, self.size_pop, self.len_chrom

    cxpoint1, cxpoint2 = torch.randint(0, self.len_chrom - 1, (2, size_pop // 2), device=self.device)
    cxswap = cxpoint1 >= cxpoint2
    cxpoint1[cxswap], cxpoint2[cxswap] = cxpoint2[cxswap], cxpoint1[cxswap] + 1
    pos_recorder = torch.zeros_like(self.Chrom)
    pos_recorder[
        torch.arange(
            len(pos_recorder.view(-1)), device=self.device).reshape([-1, self.len_chrom]) // self.len_chrom,
        self.Chrom.long()
    ] = torch.arange(self.len_chrom).type_as(self.Chrom)
    # carlist cross
    for i in range(0, self.len_chrom):
        sel = torch.where((cxpoint1 <= i) & (i < cxpoint2))[0]
        sel1, sel2 = sel * 2, sel * 2 + 1

        value1, value2 = self.Chrom[sel1, i], self.Chrom[sel2, i]
        pos1, pos2 = pos_recorder[sel1, value2], pos_recorder[sel2, value1]
        self.Chrom[sel1, i], self.Chrom[sel1, pos1] = self.Chrom[sel1, pos1], self.Chrom[sel1, i]
        self.Chrom[sel2, i], self.Chrom[sel2, pos2] = self.Chrom[sel2, pos2], self.Chrom[sel2, i]
        pos_recorder[sel1, value1], pos_recorder[sel1, value2] = pos_recorder[sel1, value2], i
        pos_recorder[sel2, value1], pos_recorder[sel2, value2] = i, pos_recorder[sel2, value1]

    # deltaT cross

    return self.Chrom
